package munci.old.learn;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import munci.old.java.Signature;
import munci.old.java.Type;
import munci.old.know.Env;
import munci.old.know.Stack;
import munci.old.know.Dictionary.Bookmark;
import munci.old.know.words.Constant;
import munci.old.know.words.MetaWord;
import munci.old.know.words.MethodWord;
import munci.old.know.words.Word;
import munci.old.use.Tokens;
import munci.old.use.Visitor;
import munci.read.Token;


/**
 * Most of the words that use Env.get()
 * @author Giles Burgess
 *
 */
public class Statics {

	public static final Word[] WORDS = scanWords(Statics.class);

	@MethodWord.Name(":") public static Token.Identifier ident() {
		return identifier(Env.get());
	}
	
	static Token.Identifier identifier(Env env) {
		Token.Identifier tok = (Token.Identifier) env.token();
		env.nextToken();
		return tok;
	}

	@MethodWord.Name("$") public static void stack() {
		Env.get().stack.print(System.out);
	}
	
	@MethodWord.Name("words") public static void words() {
		final int[] count = new int[1];
		
		Env.get().dict.visit(new Visitor<Word>() {
			String last = "";
			@Override public boolean visit(Word entry) {
				if (!last.equals(entry.name)) {
					System.out.print(entry.name + " ");
					last = entry.name;
				}
				count[0]++;
				return false;
			}
		});
		System.out.println("\n" + count[0] + " words");
	}
	
	@MethodWord.Name("about") public static void about(CharSequence str) {
		Env.get().dict.search(str.toString(), new Visitor<Word>() {
			@Override public boolean visit(Word entry) {
				System.out.println(entry);
				return false;
			}
		});
	}
	
	@MethodWord.Name("import") public static Type.Ref type() {
		return Type.ROOT.dot(identifier(Env.get()).toString());
	}
	
	@MethodWord.Name(".") public static Type.Ref dot(Type.Ref type) {
		return type.dot(identifier(Env.get()).toString());
	}

	@MethodWord.Name(";") public static void declare(Type.Ref type) {
		Env.get().dict.learn(new Constant(type.part(), type, Type.Ref.class));
	}

	@MethodWord.Name("find") public static Word[] find(CharSequence name) {
		final List<Word> words = new LinkedList<Word>();
		Env.get().dict.search(name.toString(), new Visitor<Word>() {
			@Override public boolean visit(Word entry) {
				words.add(entry);
				return false;
			}
		});
		return words.toArray(new Word[0]);
	}

	// The name of this method must /not/ be changed
	@MethodWord.Name("scanwords") public static final Word[] scanWords(Class<?> type) {
		return MetaWord.all(MethodWord.all(new Word.Book(type))).toArray();
	}
	
	private static int idx = 1;
	
	@MethodWord.Name("is") public static void define(Stack stack, final CharSequence name) {
		final Signature sig = new Signature(stack);
		Env env = Env.get();
		final Bookmark mark = env.dict.bookmark();
		final String type = "Generated" + idx++;
		final Tokens toks =	new Tokens().lex(
				"public class " + type + " { " +
				"public static final monkey.know.words.Word[] WORDS = " +
				"monkey.learn.Statics.scanWords(" + type +".class);" +
					"@monkey.know.words.MethodWord.Name(\"" + 
					Literals.encodeString(name.toString()) +
					"\") public static ")
					.addAll(sig.toJava("run"));
		final Set<Tokens> args = new HashSet<Tokens>();

		final Word end = new Word(";") {
			public boolean execute(Env env) {
				mark.forget();
				env.setCompiling(false);
				toks.add("{");
				recurse(env.stack, toks, sig.returns != Type.VOID);
				toks.add("}").add("}");
//				env.stack.push(toks, Tokens.class);
				try {
					env.dict.learn(env.dict.compile(toks.toString(), type));
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
				
				return true;
			}
			void recurse(Stack stack, Tokens toks, boolean ret) {
				Object top = stack.pop();
				if (top == this) return;
				recurse(stack, toks, false);
				if (ret)
					toks.add("return");
				if (ret || !args.contains(top)) {
					toks.addAll((Tokens) top).add(";");
				}
			}
			@Override public String toString() {
				return toks.toString();
			}
		};
		
		env.stack.push(end, Word.class);
		for (int i = 0; i < sig.names.length; i++) {
			final String param = sig.names[i];
			if (param == null) {
				Tokens arg = new Tokens().add("$" + i);
				args.add(arg);
				env.stack.push(arg, Tokens.class);
			} else {
				env.dict.learn(new Word(param) {
					public boolean execute(Env env) {
						env.stack.push(new Tokens().add(param), Tokens.class);
						return true;
					}
				});
			}
		}
		env.setCompiling(true);
		env.dict.learn(end);
	}
}
